﻿using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;

namespace SmartFront.Toolkit.ActionFramework
{
    internal static class ActionExtensionMethods
    {
        public static MethodInfo FindMethod<TInterface, TResult>(this Action<TResult> action)
        {
            MethodInfo[] methods = typeof (TInterface).GetMethods();

            // Get the ServiceInfo attribute to read properties like the Operation Name
            Type currentActionType = action.GetType();
            object[] classAttributes = currentActionType.GetCustomAttributes(typeof (OperationInfoAttribute), false);
            Debug.Assert(1 == classAttributes.Length);
            var serviceInfo = classAttributes[0] as OperationInfoAttribute;

            // Also read the properties of the Action instance marked with the OperationParameter attribute 
            PropertyInfo[] propInfoArr = currentActionType.GetProperties();

            // Fill the operation parameter types in the dictionary - paramTypes (in the correct order as the dictionary key)
            var paramTypes = (from propInfo in propInfoArr
                              let propAttributes =
                                  propInfo.GetCustomAttributes(typeof (OperationParameterInfoAttribute), false)
                              where propAttributes.Length == 1
                              let operationParam = propAttributes[0] as OperationParameterInfoAttribute
                              where null != operationParam
                              select new {operationParam.Order, Type = propInfo.PropertyType})
                              .ToDictionary(paramType => paramType.Order, paramType => paramType.Type);

            // For each of the methods check if the method name and the method signature matches the required one.
            return (from methodInfo in methods
                    where serviceInfo != null && methodInfo.Name == serviceInfo.OperationName
                    let parameters = methodInfo.GetParameters()
                    where paramTypes.Count == parameters.Length
                    where (typeof (Void) == typeof (TResult) 
                    && typeof (void) == methodInfo.ReturnType) || methodInfo.ReturnType == typeof (TResult)
                    let isMatch = parameters.All(param => param.ParameterType == paramTypes[param.Position])
                    where isMatch
                    select methodInfo).FirstOrDefault();
        }
    }
}